<template>
    <div class="wrapper">
        <div class="map" id="map"></div>

        <div class="btnList">
            <el-button type="primary" @click="menuClick('Circle')">圆形框选</el-button>
            <el-button type="primary" @click="menuClick('Box')">矩形框选</el-button>
            <el-button type="primary" @click="menuClick('Polygon')">多边形框选</el-button>
            <el-button type="primary" @click="clear">清空</el-button>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { nanoid } from 'nanoid';
import { Feature, Map, View } from 'ol';
import { fromLonLat, transform } from 'ol/proj';
import { Circle, LineString, Point } from 'ol/geom';
import { DragBox, Draw, Snap } from 'ol/interaction';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import { XYZ } from 'ol/source';
import VectorSource from 'ol/source/Vector';
import { onMounted, onUnmounted, ref } from 'vue';
import Polygon, { fromCircle } from 'ol/geom/Polygon';
import { createBox, DrawEvent } from 'ol/interaction/Draw';
import GeoJSON from 'ol/format/GeoJSON';
import { platformModifierKeyOnly } from 'ol/events/condition';
import { Fill, Stroke, Style, Icon } from 'ol/style';

const map = ref();
const source = ref();
const layer = ref();
const uid = ref(nanoid());
const draw = ref();
const snap = ref();
const modify = ref();

onMounted(() => {
    map.value = new Map({
        target: 'map',
        view: new View({
            center: fromLonLat([116.4, 39.9]),
            zoom: 12,
            projection: 'EPSG:3857'
        }),
        layers: [
            new TileLayer({
                source: new XYZ({
                    url: `http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7`
                })
            })
        ]
    });

    source.value = new VectorSource({ wrapX: true });
    layer.value = new VectorLayer({
        source: source.value,
        style: {
            'fill-color': 'rgba(200,200,200,0.2)',
            'stroke-color': 'red',
            'stroke-width': 5,
            'circle-radius': 10,
            'circle-fill-color': '#ff0000',
            'stroke-line-dash': [10, 10]
        }
    });
    layer.value.set('uid', uid.value);
    map.value.addLayer(layer.value);

    addFeature(fromLonLat([116.38935804066813, 39.910730485893114]), 'Point');
    addFeature(fromLonLat([116.37167804066813, 39.868050485893114]), 'Point');
    addFeature(fromLonLat([116.31537804066813, 39.897560485893114]), 'Point');

    snap.value = new Snap({
        source: source.value
    });
    map.value.addInteraction(snap.value);

    const dragBox = new DragBox({
        condition: platformModifierKeyOnly,
        className: 'dragBox'
    });
    map.value.addInteraction(dragBox);

    dragBox.on('boxend', () => {
        const extent = dragBox.getGeometry().getExtent();
        console.log(extent, 'boxend=');

        const selectedFeatures = source.value.getFeaturesInExtent(extent);
        selectedFeatures.forEach((feature: Feature) => {
            feature.setStyle(
                new Style({
                    stroke: new Stroke({
                        color: '#00ff00',
                        width: 2
                    }),
                    fill: new Fill({ color: '#fff000' }),
                    image: new Icon({
                        src: new URL(
                            '@/assets/map-point-active.png',
                            import.meta.url
                        ).href,
                        width: 48,
                        height: 48
                    })
                })
            );
            console.log(feature.getProperties(), 'feature===');
        });
    });
});

function menuClick(type: 'Circle' | 'Polygon' | 'Box') {
    draw.value = new Draw({
        type: type === 'Box' ? 'Circle' : type,
        source: source.value,
        geometryFunction: type === 'Box' ? createBox() : undefined
    });

    draw.value.on('drawend', (e: DrawEvent) => {
        console.log(e.feature);
        draw.value.setActive(false);
        const geom = e.feature.getGeometry();
        const proj = map.value.getView().getProjection().getCode();
        let geometry;
        if (
            geom instanceof Polygon ||
            geom instanceof LineString ||
            geom instanceof Point
        ) {
            geometry = geom.clone();
        } else if (geom instanceof Circle) {
            geometry = fromCircle(geom);
        }

        let geoJson = '';
        if (geometry) {
            const geoObj = new GeoJSON();
            geoJson =
                proj != 'EPSG:4326'
                    ? geoObj.writeGeometry(
                          geometry.transform(proj, 'EPSG:4326')
                      )
                    : geoObj.writeGeometry(geometry);
            geometry.dispose();
        }

        if (geom instanceof Circle) {
            console.log(
                geoJson,
                geom.getRadius(),
                transform(geom.getCenter(), 'EPSG:3857', 'EPSG:4326')
            );
        } else {
            console.log(geoJson);
        }

        source.value.forEachFeatureIntersectingExtent(
            geom?.getExtent(),
            function (feature: Feature) {
                if (
                    geom?.intersectsCoordinate(
                        feature?.getGeometry()?.getCoordinates()
                    )
                ) {
                    console.log(feature.getProperties(), 'feature===');
                }
            }
        );
    });
    map.value.addInteraction(draw.value);
}

function clear() {
    if (modify.value) {
        modify.value.setActive(false);
        map.value.removeInteraction(modify.value);
        modify.value.dispose();
    }
    if (draw.value) {
        map.value.removeInteraction(draw.value);
        draw.value.dispose();
    }
    layer.value.getSource().clear(true);
}

onUnmounted(() => {
    if (draw.value) {
        map.value.removeInteraction(draw.value);
        draw.value.dispose();
    }
    if (layer.value) {
        layer.value.getSource().clear(true);
        map.value.removeLayer(layer.value);
        layer.value.dispose();
    }
});

function addFeature(
    data: number[] | number[][] | number[][][],
    type: 'Point' | 'LineString' | 'Polygon' | 'Circle',
    radius?: number
) {
    const feature = new Feature({
        type: type,
        geometry: getGeometry(data, type, radius)
    });
    source.value.addFeature(feature);
}

function getGeometry(
    data: number[] | number[][] | number[][][],
    type: 'Point' | 'LineString' | 'Polygon' | 'Circle',
    radius?: number
) {
    if (type === 'Point') {
        return new Point(data as Coordinate);
    } else if (type === 'LineString') {
        return new LineString(data as Coordinate[]);
    } else if (type === 'Circle') {
        return new Circle(data as number[], radius);
    } else if (type === 'Polygon') {
        return new Polygon(data as Coordinate[][]);
    }
    throw new Error('No Type');
}
</script>

<style lang="scss">
.dragBox {
    background-color: rgba(7, 193, 96, 0.4);
}
.wrapper {
    height: 100%;
    position: relative;
    .map {
        height: 100%;
    }
    .btnList {
        position: absolute;
        top: 60px;
        left: 100px;
    }
}
</style>